﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Xceed.Silverlight.Data;
using Xceed.Silverlight.Utils;
using Xceed.Silverlight.DragDrop;

namespace Xceed.Silverlight.ListBox
{
  [TemplateVisualState( GroupName = "CommonStates", Name = "Disabled" )]
  [TemplateVisualState( GroupName = "CommonStates", Name = "Normal" )]
  [TemplateVisualState( GroupName = "CommonStates", Name = "MouseOver" )]
  [TemplateVisualState( GroupName = "SelectionStates", Name = "Unselected" )]
  [TemplateVisualState( GroupName = "SelectionStates", Name = "Selected" )]
  [TemplateVisualState( GroupName = "SelectionStates", Name = "SelectedUnfocused" )]
  [TemplateVisualState( GroupName = "FocusStates", Name = "Unfocused" )]
  [TemplateVisualState( GroupName = "FocusStates", Name = "Focused" )]
  [TemplateVisualState( GroupName = "LoadingStates", Name = "Loaded" )]
  [TemplateVisualState( GroupName = "LoadingStates", Name = "Loading" )]
  [TemplateVisualState( GroupName = "LayoutModeStates", Name = "Vertical" )]
  [TemplateVisualState( GroupName = "LayoutModeStates", Name = "Horizontal" )]
  [TemplateVisualState( GroupName = "LayoutModeStates", Name = "Floating" )]
  public class ListBoxContainer : Control, IProvideDraggedContentPresenter
  {
    #region Constructors

    protected ListBoxContainer()
    {
      this.DefaultStyleKey = typeof( ListBoxContainer );
      this.Translation = new TranslateTransform();
      this.Rotation = new RotateTransform();
      this.Scale = new ScaleTransform();
      this.TransformationGroup.Children.Add( this.Translation );
      this.TransformationGroup.Children.Add( this.Scale );
      this.TransformationGroup.Children.Add( this.Rotation );
      this.RenderTransform = this.TransformationGroup;
      this.IsDirty = true;

      this.Loaded += this.OnLoaded;
      this.Unloaded += this.OnUnloaded;
      this.IsEnabledChanged += this.OnIsEnabledChanged;
    }

    #endregion

    #region Content Property

    public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
      "Content",
      typeof( object ),
      typeof( ListBoxContainer ),
      new PropertyMetadata( null ) );

    public object Content
    {
      get
      {
        return ( object )this.GetValue( ListBoxContainer.ContentProperty );
      }
      set
      {
        this.SetValue( ListBoxContainer.ContentProperty, value );
      }
    }

    #endregion

    #region ContentTemplate Property

    public static readonly DependencyProperty ContentTemplateProperty = DependencyProperty.Register(
      "ContentTemplate",
      typeof( DataTemplate ),
      typeof( ListBoxContainer ),
      new PropertyMetadata( null ) );

    public DataTemplate ContentTemplate
    {
      get
      {
        return ( DataTemplate )this.GetValue( ListBoxContainer.ContentTemplateProperty );
      }
      set
      {
        this.SetValue( ListBoxContainer.ContentTemplateProperty, value );
      }
    }

    #endregion

    #region DataPath Internal Property

    // Use for Selection
    internal DataPath DataPath
    {
      get
      {
        return m_dataPath;
      }
      set
      {
        if( m_dataPath != value )
        {
          m_dataPath = value;
          if( m_dataPath != null )
          {
            this.IsDataPathLoaded = m_dataPath.LastChild.IsLoaded;
          }
          else
          {
            this.IsDataPathLoaded = false;
          }
        }
      }
    }

    private DataPath m_dataPath; 
    #endregion

    #region IsFocused Internal Property

    internal bool IsFocused
    {
      get
      {
        return m_isFocused;
      }
      set
      {
        m_isFocused = value;
      }
    }

    private bool m_isFocused;

    #endregion

    #region IsVisuallyFocused Internal Property

    internal bool IsVisuallyFocused
    {
      get
      {
        return m_isVisuallyFocused;
      }
      set
      {
        if( m_isVisuallyFocused != value )
        {
          m_isVisuallyFocused = value;
          this.UpdateFocusStates( true );
        }
      }
    }

    private bool m_isVisuallyFocused;

    #endregion

    #region IsDataPathLoaded
    internal bool IsDataPathLoaded
    {
      get
      {
        return m_isDataPathLoaded;
      }
      set
      {
        if( m_isDataPathLoaded != value )
        {
          m_isDataPathLoaded = value;
          this.UpdateLoadingStates( true );
        }
      }
    }

    private bool m_isDataPathLoaded;
    #endregion

    #region IsMouseOver Internal Property

    internal bool IsMouseOver
    {
      get
      {
        return m_isMouseOver;
      }
      set
      {
        if( m_isMouseOver != value )
        {
          m_isMouseOver = value;
          this.UpdateCommonStates( true );
        }
      }
    }

    private bool m_isMouseOver;

    #endregion

    #region IsSelectable Internal Property

    internal virtual bool IsSelectable
    {
      get
      {
        return true;
      }
    }

    #endregion

    #region IsSelected Property

    public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register(
      "IsSelected",
      typeof( bool ),
      typeof( ListBoxContainer ),
      new PropertyMetadata(
        ( bool )false,
        new PropertyChangedCallback( ListBoxContainer.OnIsSelectedChanged ) ) );

    public bool IsSelected
    {
      get
      {
        return ( bool )this.GetValue( ListBoxContainer.IsSelectedProperty );
      }
      set
      {
        if( this.IsSelected != value )
        {
          var parentListBox = this.ParentListBox;

          if( parentListBox != null )
          {
            // The listbox UpdateSelection will end up calling SetIsSelectedInternal which will
            // properly update the IsSelectedProperty.
            parentListBox.UpdateSelection( this, SelectionAction.CodeBehind );
          }
        }
      }
    }

    internal void SetIsSelectedInternal( bool isSelected )
    {
      this.SetValue( ListBoxContainer.IsSelectedProperty, isSelected );
    }

    private static void OnIsSelectedChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBoxContainer container = sender as ListBoxContainer;

      if( container == null )
        return;

      container.OnIsSelectedChanged( ( bool )e.OldValue, ( bool )e.NewValue );
    }

    #endregion

    #region ParentListBox Property

    public ListBox ParentListBox
    {
      get
      {
        return ListBox.GetParentListBox( this );
      }
    }

    #endregion


    #region SelectionRange Property

    internal SelectionRange SelectionRange
    {
      get;
      set;
    }

    #endregion

    #region Rotation Property

    internal static readonly DependencyProperty RotationProperty = DependencyProperty.Register(
      "Rotation",
      typeof( RotateTransform ),
      typeof( ListBoxContainer ),
      new PropertyMetadata( ( RotateTransform )null ) );

    internal RotateTransform Rotation
    {
      get
      {
        return ( RotateTransform )this.GetValue( ListBoxContainer.RotationProperty );
      }
      set
      {
        this.SetValue( ListBoxContainer.RotationProperty, value );
      }
    }

    #endregion

    #region Translation Property

    internal static readonly DependencyProperty TranslationProperty = DependencyProperty.Register(
      "Translation",
      typeof( TranslateTransform ),
      typeof( ListBoxContainer ),
      new PropertyMetadata( null ) );

    internal TranslateTransform Translation
    {
      get
      {
        return ( TranslateTransform )this.GetValue( ListBoxContainer.TranslationProperty );
      }
      private set
      {
        this.SetValue( ListBoxContainer.TranslationProperty, value );
      }
    }

    #endregion

    #region Scale Property

    internal static readonly DependencyProperty ScaleProperty = DependencyProperty.Register(
      "Scale",
      typeof( ScaleTransform ),
      typeof( ListBoxContainer ),
      new PropertyMetadata( null ) );

    internal ScaleTransform Scale
    {
      get
      {
        return ( ScaleTransform )this.GetValue( ListBoxContainer.ScaleProperty );
      }
      set
      {
        this.SetValue( ListBoxContainer.ScaleProperty, value );
      }
    }

    #endregion

    #region ZOrder Property

    internal static readonly DependencyProperty ZOrderProperty = DependencyProperty.Register(
      "ZOrder",
      typeof( int ),
      typeof( ListBoxContainer ),
      new PropertyMetadata(
        ( int )0,
        new PropertyChangedCallback( ListBoxContainer.OnZOrderChanged ) ) );

    internal int ZOrder
    {
      get
      {
        return ( int )this.GetValue( ListBoxContainer.ZOrderProperty );
      }
      set
      {
        this.SetValue( ListBoxContainer.ZOrderProperty, value );
      }
    }

    private static void OnZOrderChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBoxContainer container = sender as ListBoxContainer;

      if( container != null )
        container.SetValue( Canvas.ZIndexProperty, e.NewValue );
    }

    #endregion

    #region Container Configuration Property
    internal ListBoxContainerConfigurationBase Configuration
    {
      get;
      set;
    }
    #endregion

    #region AbsoluteOpacity Property
    internal double AbsoluteOpacity
    {
      get;
      set;
    }
    #endregion

    #region CurrentOffset Property
    internal Point CurrentOffset
    {
      get
      {
        return m_currentPosition;
      }
    }
    #endregion

    #region AbsoluteScaleFactor Property
    internal double AbsoluteScaleFactor
    {
      get
      {
        return m_absoluteScaleFactor;
      }
      set
      {
        if( value != m_absoluteScaleFactor )
        {
          m_absoluteScaleFactor = value;
        }
      }
    }
    #endregion

    #region AbsoluteRotationAngle Property

    internal double AbsoluteRotationAngle
    {
      get
      {
        return m_absoluteRotationAngle;
      }
      set
      {
        double oldValue = m_absoluteRotationAngle;
        if( value != oldValue )
        {
          m_absoluteRotationAngle = value;
        }
      }
    }

    #endregion

    #region AbsoluteOffset Property

    internal Point AbsoluteOffset
    {
      get
      {
        return m_absoluteOffset;
      }
      set
      {
        Point oldValue = m_absoluteOffset;
        if( value != oldValue )
        {
          m_absoluteOffset = value;
        }
      }
    }

    #endregion

    #region AbsoluteElementHeight Property

    internal double AbsoluteElementHeight
    {
      get
      {
        return this.ComputeElementHeight( false );
      }
    }

    #endregion

    #region ActualElementHeight Property

    internal double ActualElementHeight
    {
      get
      {
        return this.ComputeElementHeight( true );
      }
    }

    #endregion

    #region AbsoluteElementWidth Property

    internal double AbsoluteElementWidth
    {
      get
      {
        return this.ComputeElementWidth( false );
      }
    }

    #endregion

    #region ActualElementWidth Property

    internal double ActualElementWidth
    {
      get
      {
        return this.ComputeElementWidth( true );
      }
    }

    #endregion

    #region AbsoluteMaximumHeight Property

    internal double AbsoluteMaximumHeight
    {
      get
      {
        return m_maximumHeight;
      }
    }

    #endregion

    #region AbsoluteMaximumWidth Property

    internal double AbsoluteMaximumWidth
    {
      get
      {
        return m_maximumWidth;
      }
    }

    #endregion

    #region DesiredElementHeight Property

    internal double DesiredElementHeight
    {
      get
      {
        if( this.Configuration == null )
          return 0;

        return this.Configuration.CachedSize.Height;
      }
    }

    #endregion

    #region DesiredElementHeight Property

    internal double DesiredElementWidth
    {
      get
      {
        if( this.Configuration == null )
          return 0;

        return this.Configuration.CachedSize.Width;
      }
    }

    #endregion

    #region MaximumHeight Property
    internal double MaximumHeight
    {
      get
      {
        double retval = m_maximumHeight;

        SizeAnimation sizeAnimation = this.GetAnimation( typeof( SizeAnimation ) ) as SizeAnimation;

        if( sizeAnimation != null )
        {
          retval += sizeAnimation.ActualMaximumHeightChange;
        }

        m_currentMaximumHeight = retval;

        return retval;
      }
      set
      {
        if( value != m_maximumHeight )
        {
          m_maximumHeight = value;

          if( m_deferSizeUpdateCount == 0 )
          {
            this.UpdateSize();
          }
          //as opposed to the Header and Footer Size, this size do not affect the Layout Manager (it is in fact fed by this one).
        }
      }
    }

    #endregion

    #region Maximum Width Property
    internal double MaximumWidth
    {
      get
      {
        double retval = m_maximumWidth;

        SizeAnimation sizeAnimation = this.GetAnimation( typeof( SizeAnimation ) ) as SizeAnimation;

        if( sizeAnimation != null )
        {
          retval += sizeAnimation.ActualMaximumWidthChange;
        }

        this.m_currentMaximumWidth = retval;

        return retval;
      }
      set
      {
        if( value != m_maximumWidth )
        {
          m_maximumWidth = value;

          if( m_deferSizeUpdateCount == 0 )
          {
            this.UpdateSize();
          }
          //as opposed to the Header and Footer Size, this size do not affect the Layout Manager (it is in fact fed by this one).
        }
      }
    }
    #endregion

    #region InternalArrangeSize Property

    private Size InternalArrangeSize
    {
      get
      {
        return new Size( this.ActualElementWidth, this.ActualElementHeight );
      }
    }

    #endregion

    #region ActualComputedPosition Property

    internal Point ActualComputedPosition
    {
      get
      {
        return new Point( this.Translation.X, this.Translation.Y );
      }
    }

    #endregion

    #region TransformationGroup Property
    internal TransformGroup TransformationGroup
    {
      get
      {
        return m_transformationGroup;
      }
    }
    #endregion

    #region IsDirty Property
    internal bool IsDirty
    {
      get;
      set;
    }
    #endregion

    #region LayoutMode Attached Property

    private static void OnLayoutModeChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBoxContainer listBoxContainer = sender as ListBoxContainer;

      if( listBoxContainer != null )
      {
        Debug.Assert( ( ContainerLayoutMode )e.NewValue != ContainerLayoutMode.Default );
        listBoxContainer.UpdateLayoutModeStates( true );
      }
    }

    public static ContainerLayoutMode GetLayoutMode( DependencyObject obj )
    {
      return ( ContainerLayoutMode )obj.GetValue( ListBoxContainer.LayoutModeProperty );
    }

    public static void SetLayoutMode( DependencyObject obj, ContainerLayoutMode value )
    {
      obj.SetValue( ListBoxContainer.LayoutModeProperty, value );
    }

    public static readonly DependencyProperty LayoutModeProperty =
        DependencyProperty.RegisterAttached( "LayoutMode", typeof( ContainerLayoutMode ), typeof( ListBoxContainer ), 
        new PropertyMetadata( ContainerLayoutMode.Default, new PropertyChangedCallback( ListBoxContainer.OnLayoutModeChanged ) ) );

    #endregion

    #region Internal PositioningLayoutMode Property

    internal ContainerLayoutMode PositioningLayoutMode
    {
      get
      {
        return m_positioningLayoutMode;
      }
      set
      {
        if( m_positioningLayoutMode != value )
        {
          if( value == ContainerLayoutMode.Default )
            throw new InvalidOperationException( "PositioningLayoutMode should never be set to ContainerLayoutMode.Default." );

          m_positioningLayoutMode = value;
        }
      }
    }

    #endregion


    #region Public Methods

    public override void OnApplyTemplate()
    {
      base.OnApplyTemplate();

      ListBox parentListBox = this.ParentListBox;

      // Force the ListBox to clear its configuration
      // and reset the layout as soon as possible
      if( parentListBox != null )
        parentListBox.DeferClearContainerConfigurationCache();

      this.ChangeVisualState( false );
    }

    protected override void OnKeyDown( KeyEventArgs e )
    {
      base.OnKeyDown( e );

      if( this.ParentListBox != null )
        this.ParentListBox.Panel.ProcessListBoxContainerKeyDown( this, e );
    }

    #endregion Public Methods

    #region Protected Methods

    protected virtual DragDropContext CreateDragDropContextCore()
    {
      ListBox parentListBox = this.ParentListBox;

      View activeView = null;

      DraggedElementConfiguration draggedElementConfiguration = ( activeView != null )
        ? activeView.DraggedElementConfiguration
        : parentListBox.ContainerConfigurations.DraggedElementConfiguration;

      Debug.Assert( draggedElementConfiguration != null, "We should always have an instance of a DraggedElementConfiguration to pass to the DragContext." );

      IEnumerable<FrameworkElement> siblingDraggedElements = null;

      if( draggedElementConfiguration != null )
      {
        siblingDraggedElements = this.GetSiblingDraggedElements();
      }

      return new DragDropContext( this, draggedElementConfiguration, siblingDraggedElements );
    }

    protected virtual IEnumerable<FrameworkElement> GetSiblingDraggedElementsCore()
    {
      ListBox parentListBox = this.ParentListBox;

      if( parentListBox == null )
        return null;

      PanelBase panelBase = parentListBox.Panel;

      if( panelBase == null )
        return null;

      List<FrameworkElement> siblingElements = null;

      if( panelBase != null )
      {
        // Get a list of all ListBoxContainer that are currently in the Visual Tree
        // and have IsSelected = true
        UIElementCollection children = panelBase.Children;

        foreach( ListBoxContainer listBoxContainer in children )
        {
          if( listBoxContainer == this )
            continue;

          if( ( listBoxContainer.IsSelected ) && ( this.GetCalculatedCanDrag() ) )
          {
            if( siblingElements == null )
            {
              siblingElements = new List<FrameworkElement>();
            }

            siblingElements.Add( listBoxContainer );
          }
        }
      }

      return siblingElements;
    }

    internal void OnIsEnabledChanged( object sender, DependencyPropertyChangedEventArgs e )
    {
      this.UpdateCommonStates( true );
    }

    internal void OnIsSelectedChanged( bool oldValue, bool newValue )
    {
      if( !this.IsSelectable && newValue )
        throw new InvalidOperationException( "Can't select a non-selectable container." );

      this.UpdateSelectionStates( false );

      ListBox parentListBox = this.ParentListBox;

      if( parentListBox != null )
        parentListBox.OnListBoxContainerIsSelectedChanged( this );
    }

    protected virtual void OnLoaded()
    {
    }

    protected virtual void OnUnloaded()
    {
    }

    protected override void OnGotFocus( RoutedEventArgs e )
    {
      base.OnGotFocus( e );
      bool hasFocus = FocusHelper.HasFocus( this );
      this.FocusChanged( hasFocus );
    }

    protected override void OnLostFocus( RoutedEventArgs e )
    {
      base.OnLostFocus( e );
      bool hasFocus = FocusHelper.HasFocus( this );
      this.FocusChanged( hasFocus );
    }

#if !WINDOWS_PHONE
    protected override void OnMouseEnter( MouseEventArgs e )
    {
      base.OnMouseEnter( e );
      this.IsMouseOver = true;
    }

    protected override void OnMouseLeave( MouseEventArgs e )
    {
      base.OnMouseLeave( e );
      this.IsMouseOver = false;
    }

    protected override void OnMouseLeftButtonDown( MouseButtonEventArgs e )
    {
      base.OnMouseLeftButtonDown( e );

      if( e.Handled )
        return;

      ListBox parentListBox = this.ParentListBox;
      Debug.Assert( this.ParentListBox != null );

      if( parentListBox == null )
        return;

      e.Handled = true;

      if( this.Focus() )
      {
        DataPath dataPath = this.DataPath;

        // Do not affect selection when the DataPath is not loaded. A 
        // Prepare container will be called when the replace is processed and
        // the focused container will be the one matching the focused DataPath
        if( ( dataPath != null ) && ( dataPath.LastChild != null ) && ( dataPath.LastChild.IsLoaded ) )
          parentListBox.UpdateSelection( this, SelectionAction.MouseDown );
      }

      parentListBox.Panel.ProcessListBoxContainerMouseDown( this, e, !this.GetHasRunningTransitionAnimation() );

      if( this.GetCalculatedCanDrag() )
      {
        DragDropContext dragDropContext = this.CreateDragDropContext();

        dragDropContext.DragStarting += new EventHandler<DragDropCancelEventArgs>( this.OnDragDropContextDragStarting );

        DragDropManager.PrepareDrag( dragDropContext, e );
      }
    }

    private void OnDragDropContextDragStarting( object sender, DragDropCancelEventArgs e )
    {
      ListBox parentListBox = this.ParentListBox;

      if( parentListBox != null )
      {
        // Forward the event to the ListBox
        parentListBox.OnDragStarting( e );
      }
      else
      {
        // Can't see how this could happen, if it does cancel.
        Debug.Assert( false, "How could this happen?" );
        e.Cancel = true;
      }
    }

    protected override void OnMouseLeftButtonUp( MouseButtonEventArgs e )
    {
      base.OnMouseLeftButtonUp( e );

      ListBox parentListBox = this.ParentListBox;

      if( parentListBox == null )
        return;

      DragDropContext currentDragDropContext = DragDropManager.CurrentDragDropContext;

      if( ( currentDragDropContext == null ) ||
        ( ( currentDragDropContext != null ) && ( currentDragDropContext.DraggedElement != this ) ) )
      {
        parentListBox.UpdateSelection( this, SelectionAction.MouseUp );
      }
    }

    protected override void OnMouseWheel( MouseWheelEventArgs e )
    {
      ListBox listBox = this.ParentListBox;

      if( listBox != null )
      {
        PanelBase panel = listBox.Panel;

        if( panel != null )
        {
          panel.ProcessMouseWheel( e );
          e.Handled = true;
        }
      }

      // Call base after the ParentListBox to be able to handle
      // the wheel action before the IScrollInfo implementation of the 
      // PanelBase does.
      base.OnMouseWheel( e );
    }
#else
    protected override void OnManipulationStarted( ManipulationStartedEventArgs e )
    {
      base.OnManipulationStarted( e );

      if( e.Handled == true )
        return;

      ListBox parentListBox = this.ParentListBox;
      Debug.Assert( this.ParentListBox != null );

      if( parentListBox == null )
        return;

      e.Handled = true;

      parentListBox.Panel.InitializeManipulation();
    }

    protected override void OnManipulationDelta( ManipulationDeltaEventArgs e )
    {
      base.OnManipulationDelta( e );

      if( e.Handled == true )
        return;

      this.ParentListBox.Panel.ApplyManipulationDelta( e.DeltaManipulation.Translation, e.CumulativeManipulation.Translation, this.ActualHeight, this.ActualWidth );
    }

    protected override void OnManipulationCompleted( ManipulationCompletedEventArgs e )
    {
      base.OnManipulationCompleted( e );

      if( e.Handled == true )
        return;

      ListBox parentListBox = this.ParentListBox;

      if( parentListBox == null )
        return;

      e.Handled = true;

      if( GetIsManipulationEmpty( e ) )
      {
        // This is a tap manipulation, so do both MouseDown and MouseUp
        parentListBox.Panel.ProcessListBoxContainerMouseDown( this, null, !this.GetHasRunningTransitionAnimation() );
        parentListBox.UpdateSelection( this, SelectionAction.MouseDown );
        parentListBox.UpdateSelection( this, SelectionAction.MouseUp );
      }

      this.ParentListBox.Panel.CompleteManipulation( e.IsInertial, e.TotalManipulation, e.FinalVelocities, this.ActualHeight, this.ActualWidth );
    }
#endif

    protected override Size ArrangeOverride( Size finalSize )
    {
      if( finalSize != m_originalArrangeFinalSize )
      {
        m_arrangeReturnSize = base.ArrangeOverride( finalSize );

        m_originalArrangeFinalSize = finalSize;
      }

      Size newArrangeSize = this.InternalArrangeSize;

      base.ArrangeOverride( newArrangeSize );

      m_lastRealArrangeSize = newArrangeSize;

      return m_arrangeReturnSize;
    }

    protected virtual ContentPresenter CreateDraggedContentPresenter()
    {
      return new ContentPresenter();
    }

    protected virtual void PrepareDraggedContentPresenter(ContentPresenter contentPresenter)
    {
      ListBox.SetParentListBox( contentPresenter, this.ParentListBox );
    }

    protected virtual void ClearDraggedContentPresenter(ContentPresenter contentPresenter)
    {
      contentPresenter.ClearValue( ListBox.ParentListBoxProperty );
    }
    #endregion Protected Methods

    #region Internal Methods

    internal void ChangeVisualState()
    {
      this.ChangeVisualState( true );
    }

    protected internal virtual void ChangeVisualState( bool useTransitions )
    {
      this.UpdateLayoutModeStates( useTransitions );

      this.UpdateCommonStates( useTransitions );

      this.UpdateSelectionStates( useTransitions );

      this.UpdateFocusStates( useTransitions );

      this.UpdateLoadingStates( useTransitions );
    }

    internal bool GoToState( bool useTransitions, string stateName )
    {
      return VisualStateManager.GoToState( this, stateName, useTransitions );
    }

    internal virtual void SetDataContextFromDataPath( DataPath dataPath )
    {
      this.DataContext = dataPath;
      this.Content = dataPath;
    }

    internal void InvalidatePrepare()
    {
      this.IsDirty = true;
    }

    #endregion Internal Methods

    #region Internal Animations Related Methods

    internal void BeginSizeUpdateDeferring()
    {
      m_deferSizeUpdateCount++;
    }

    internal void EndSizeUpdateDeferring()
    {
      if( m_deferSizeUpdateCount == 0 )
        return;

      m_deferSizeUpdateCount--;

      if( m_deferSizeUpdateCount == 0 )
      {
        this.UpdateSize();
      }
    }

    private double ComputeElementHeight( bool includeTransitions )
    {
      double computedValue = 0.0d;
      double margins = this.Margin.Top + this.Margin.Bottom;
      double desiredElementHeightWithMargins = this.DesiredElementHeight - margins;

      if( this.PositioningLayoutMode != ContainerLayoutMode.Horizontal )
      {
        computedValue = desiredElementHeightWithMargins;
      }
      else
      {
        if( !includeTransitions )
          return m_maximumHeight - margins;

        computedValue = m_maximumHeight - margins;
      }

      double maximumHeight = m_maximumHeight;

      if( includeTransitions )
      {
        SizeAnimation sizeAnimation = this.GetAnimation( typeof( SizeAnimation ) ) as SizeAnimation;

        if( sizeAnimation != null )
        {
          computedValue += sizeAnimation.ActualHeightChange;
        }

        maximumHeight = this.MaximumHeight;
      }

      computedValue = Math.Max( computedValue, desiredElementHeightWithMargins );

      if( computedValue > maximumHeight - margins )
      {
        computedValue = maximumHeight - margins;
      }

      if( computedValue < 0d )
      {
        computedValue = 0d;
      }

      return computedValue;
    }

    private double ComputeElementWidth( bool includeTransitions )
    {
      double computedValue = 0;
      double margins = this.Margin.Left + this.Margin.Right;
      double desiredElementWidthWithMargins = this.DesiredElementWidth - margins;

      if( this.PositioningLayoutMode != ContainerLayoutMode.Vertical )
      {
        computedValue = desiredElementWidthWithMargins;
      }
      else
      {
        if( !includeTransitions )
          return m_maximumWidth - margins;

        computedValue = m_maximumWidth - margins;
      }

      double maximumWidth = m_maximumWidth;

      if( includeTransitions )
      {
        SizeAnimation sizeAnimation = this.GetAnimation( typeof( SizeAnimation ) ) as SizeAnimation;

        if( sizeAnimation != null )
        {
          computedValue += sizeAnimation.ActualWidthChange;
        }

        maximumWidth = this.MaximumWidth;
      }

      computedValue = Math.Max( computedValue, desiredElementWidthWithMargins );

      if( computedValue > maximumWidth - margins )
      {
        computedValue = maximumWidth - margins;
      }

      if( computedValue < 0d )
      {
        computedValue = 0d;
      }

      return computedValue;
    }

    internal void AddAnimation( IAnimation newAnimation )
    {
      IAnimation currentAnimation = this.GetAnimation( newAnimation.GetType() );

      // Replace the current animation of a type by the 
      // new animation of that same type
      if( currentAnimation != null )
      {
        m_animationList.Remove( currentAnimation );
      }

      m_animationList.Add( newAnimation );
    }


    internal void RemoveAllAnimations()
    {
      m_animationList.Clear();
    }


    internal void RemoveAnimation( Type animationType, TransitionAnimation transitionAnimation )
    {

      IAnimation currentAnimation = GetAnimation( animationType );

      if( ( currentAnimation != null ) && ( currentAnimation.ParentTransition == transitionAnimation ) )
        m_animationList.Remove( currentAnimation );

    }

    internal IAnimation GetAnimation( Type animationType )
    {
      foreach( IAnimation animation in m_animationList )
      {
        if( animation.GetType() == animationType )
          return animation;
      }

      return null;
    }

    internal bool IsAnimatedBy( Type animationType )
    {
      return this.GetAnimation( animationType ) != null;
    }

    internal List<Type> GetActiveAnimationsType( TransitionAnimation transition )
    {
      List<Type> animationTypeList = new List<Type>();
      foreach( IAnimation animation in m_animationList )
      {
        if( animation.ParentTransition == transition )
        {
          animationTypeList.Add( animation.GetType() );
        }
      }

      return animationTypeList;
    }

    internal void UpdateAnimations( double elapsedTime )
    {
      foreach( IAnimation animation in m_animationList )
      {
        animation.ComputeAnimationUpdate( elapsedTime );
      }
    }

    internal void UpdateLayoutTransform()
    {
      double positionX = this.AbsoluteOffset.X;
      double positionY = this.AbsoluteOffset.Y;
      double rotation = this.AbsoluteRotationAngle;
      double scaleFactor = this.AbsoluteScaleFactor;
      double width = this.ActualElementWidth;
      double height = this.ActualElementHeight;
      double opacity = this.AbsoluteOpacity;

      PositionAnimation positionAnimation = this.GetAnimation( typeof( PositionAnimation ) ) as PositionAnimation;

      if( positionAnimation != null )
      {
        positionX += positionAnimation.ActualXChange;
        positionY += positionAnimation.ActualYChange;
      }

      RotateAnimation rotateAnimation = this.GetAnimation( typeof( RotateAnimation ) ) as RotateAnimation;
      if( rotateAnimation != null )
      {
        if( !Double.IsNaN( rotateAnimation.ActualAngleChange ) )
          rotation += rotateAnimation.ActualAngleChange;
      }

      ScaleAnimation scaleAnimation = this.GetAnimation( typeof( ScaleAnimation ) ) as ScaleAnimation;
      if( scaleAnimation != null )
      {
        if( !Double.IsNaN( scaleAnimation.ActualScaleChange ) )
          scaleFactor += scaleAnimation.ActualScaleChange;
      }
      m_currentPosition.X = positionX;
      m_currentPosition.Y = positionY;

      this.Translation.X = positionX;
      this.Translation.Y = positionY;
      this.Scale.CenterX = positionX;
      this.Scale.CenterY = positionY;
      this.Scale.ScaleX = scaleFactor;
      this.Scale.ScaleY = scaleFactor;


      if( ( !Double.IsInfinity( width ) ) && ( width != double.MaxValue ) )
        this.Rotation.CenterX = positionX + ( width / 2.0d );

      if( ( !Double.IsInfinity( height ) ) && ( height != double.MaxValue ) )
        this.Rotation.CenterY = positionY + ( height / 2.0d );

      this.Rotation.Angle = rotation;

      OpacityAnimation opacityAnimation = this.GetAnimation( typeof( OpacityAnimation ) ) as OpacityAnimation;

      if( opacityAnimation != null )
      {
        if( !Double.IsNaN( opacityAnimation.ActualOpacityChange ) )
          opacity += opacityAnimation.ActualOpacityChange;
      }

      this.Opacity = opacity;
    }

    internal void UpdateSize()
    {

      Size newSize = new Size( this.ActualElementWidth, this.ActualElementHeight );

      if( m_lastRealArrangeSize != newSize )
      {
        this.InvalidateArrange();

        //TODO: the following line is to avoid the flickering caused by the re-arrangements of controls. Forces a layout pass for the concerned element
        this.UpdateLayout();
      }


    }


    #endregion

    #region Recycling

    internal void OnRecycledAndLayoutedOutOfView()
    {
      // Reset RecycleEvenIfFocused.
      m_recycleEvenIfFocused = null;
    }

    internal bool GetIsElligibleForRecycling()
    {
      if( this.GetHasRunningTransitionAnimation() )
        return false;

      if( this.RecycleEvenIfFocused )
      {
        return true;
      }

      return !FocusHelper.HasFocus( this );
    }

    internal bool RecycleEvenIfFocused
    {
      get
      {
        if( !m_recycleEvenIfFocused.HasValue )
        {
          return this.DefaultRecycleEvenIfFocused;
        }

        return m_recycleEvenIfFocused.Value;
      }
      set
      {
        m_recycleEvenIfFocused = value;
      }
    }

    internal virtual bool DefaultRecycleEvenIfFocused
    {
      get
      {
        return false;
      }
    }

    private bool? m_recycleEvenIfFocused;

    #endregion


    #region Private Methods

    private DragDropContext CreateDragDropContext()
    {
      return this.CreateDragDropContextCore();
    }

    private IEnumerable<FrameworkElement> GetSiblingDraggedElements()
    {
      return this.GetSiblingDraggedElementsCore();
    }

    private bool GetCalculatedCanDrag()
    {
      ListBoxContainerConfigurationBase configuration = this.Configuration;

      Debug.Assert( configuration != null );

      if( ( configuration != null ) && ( configuration.CanDrag ) && ( this.ParentListBox.AllowDrag )
        && ( this.IsDataPathLoaded ) && ( !this.GetHasRunningTransitionAnimation<RemovingItemTransitionAnimation>() ) )
      {
        return true;
      }

      return false;
    }

    private void OnLoaded( object sender, RoutedEventArgs e )
    {
      this.ChangeVisualState( false );
      this.OnLoaded();
    }

    private void OnUnloaded( object sender, RoutedEventArgs e )
    {
      this.ChangeVisualState( false );
      this.OnUnloaded();
    }

    private void FocusChanged( bool hasFocus )
    {
      this.IsFocused = hasFocus;
      this.IsVisuallyFocused = hasFocus;
    }

    private bool IsParentSelectionActive()
    {
      //TODO mimic ListBox verifying if at least 1 item is selected
      return ( this.ParentListBox != null ) && ( ( this.ParentListBox.Panel.LayoutManager.GetContainerFromPath( this.ParentListBox.CurrentDataPath ) != null ) );
    }

    private void UpdateCommonStates( bool useTransitions )
    {
      if( !base.IsEnabled )
      {
        this.GoToState( useTransitions, "Disabled" );
      }
      else if( this.IsMouseOver )
      {
        this.GoToState( useTransitions, "MouseOver" );
      }
      else
      {
        this.GoToState( useTransitions, "Normal" );
      }
    }

    private void UpdateSelectionStates( bool useTransitions )
    {
      if( this.IsSelected )
      {
        if( this.IsParentSelectionActive() )
        {
          this.GoToState( useTransitions, "Selected" );
        }
        else
        {
          if( !this.GoToState( useTransitions, "SelectedUnfocused" ) )
          {
            this.GoToState( useTransitions, "Selected" );
          }
        }
      }
      else
      {
        this.GoToState( useTransitions, "Unselected" );
      }
    }

    private void UpdateLoadingStates( bool useTransitions )
    {
      if( this.IsDataPathLoaded )
      {
        this.GoToState( useTransitions, "Loaded" );
      }
      else
      {
        this.GoToState( useTransitions, "Loading" );
      }

    }

    private void UpdateFocusStates( bool useTransitions )
    {
      if( this.IsVisuallyFocused )
      {
        this.GoToState( useTransitions, "Focused" );
      }
      else
      {
        this.GoToState( useTransitions, "Unfocused" );
      }
    }

    private void UpdateLayoutModeStates( bool useTransitions )
    {
      switch( ListBoxContainer.GetLayoutMode( this ) )
      {
        case ContainerLayoutMode.Vertical:
        {
          this.GoToState( useTransitions, "Vertical" );
          break;
        }

        case ContainerLayoutMode.Horizontal:
        {
          this.GoToState( useTransitions, "Horizontal" );
          break;
        }

        case ContainerLayoutMode.Floating:
        default:
        {
          this.GoToState( useTransitions, "Floating" );
          break;
        }

      }
    }

    private bool GetHasRunningTransitionAnimation()
    {
      return m_animationList.Count > 0;
    }

    private bool GetHasRunningTransitionAnimation<T>()
      where T : TransitionAnimation
    {
      foreach( IAnimation animation in m_animationList )
      {
        if( animation.ParentTransition is T )
          return true;
      }

      return false;
    }

#if WINDOWS_PHONE
    private bool GetIsManipulationEmpty( ManipulationCompletedEventArgs e )
    {
      Point emptyPoint = new Point();
      return ( e.TotalManipulation.Scale.Equals( emptyPoint ) ) &&
        ( e.TotalManipulation.Translation.Equals( emptyPoint ) ) &&
        ( ( !e.IsInertial ) ||
        ( e.FinalVelocities.ExpansionVelocity.Equals( emptyPoint ) ) &&
        ( e.FinalVelocities.LinearVelocity.Equals( emptyPoint ) ) );
    }
#endif

#if !SILVERLIGHT
    internal void InvalidateMeasureRecursively()
    {
      ListBoxContainer.InvalidateMeasureRecursively( this );
    }

    private static void InvalidateMeasureRecursively( FrameworkElement parent )
    {
      int childCount = VisualTreeHelper.GetChildrenCount( parent );

      for( int i = 0; i < childCount; i++ )
      {
        FrameworkElement child = VisualTreeHelper.GetChild( parent, i ) as FrameworkElement;
        if( child != null )
        {
          InvalidateMeasureRecursively( child );
        }
      }

      parent.InvalidateMeasure();
    }
#endif

    #endregion Private Methods

    #region Private Fields

    private Point m_absoluteOffset;
    private double m_absoluteRotationAngle = 0.0;
    private double m_absoluteScaleFactor = 1.0;
    private int m_deferSizeUpdateCount = 0;

    private Point m_currentPosition = new Point( 0.0d, 0.0d );
    private double m_currentMaximumWidth = 0.0d;
    private double m_currentMaximumHeight = 0.0d;


    private double m_maximumHeight = double.MaxValue;
    private double m_maximumWidth = double.MaxValue;
    private Size m_lastRealArrangeSize = Size.Empty;
    private Size m_originalArrangeFinalSize = Size.Empty;
    private Size m_arrangeReturnSize = Size.Empty;

    // This ContainerLayoutMode is used to calculate the height and width of the container and will
    // match the one used by the panel/active view to position the containers.  
    // This must never return ContainerLayoutMode.Default.
    //
    // On the other hand, the LayoutMode Attached Property and its corresponding local clr property is used to drive the container's 
    // visual state in order to offer a configurable way to change the container's template depending on the 
    // positioning of either the panel/active view (when set to ContainerLayoutMode.Default) or specified through 
    // the container's configuration.
    private ContainerLayoutMode m_positioningLayoutMode = ContainerLayoutMode.Floating;

    private TranslateTransform m_positionTransform = new TranslateTransform();
    private TransformGroup m_transformationGroup = new TransformGroup();

    private List<IAnimation> m_animationList = new List<IAnimation>();

    #endregion


    #region IProvideDraggedContentPresenter Members

    ContentPresenter IProvideDraggedContentPresenter.CreateDraggedContentPresenter()
    {
      ContentPresenter contentPresenter = this.CreateDraggedContentPresenter();

      Transform renderTransform = TransformHelper.CreateTransformClone( this.RenderTransform );
      this.CancelTranslateComponentsOfTransform( renderTransform );
      contentPresenter.RenderTransform = renderTransform;

      ListBox listBox = this.ParentListBox;

      if( listBox == null )
        throw new ListBoxInternalException( "A recycled container was used as drag source." );

      // We must translate the container's top left point to the top left corner of the Panel
      // since the DragDropAdornerManager will force a SetLeft and SetTop to the position
      // of the draggedElement according to the RootElement of the 
      Point itemToPanel = this.TransformToVisual( listBox.Panel ).Transform( new Point() );
      Canvas.SetLeft( contentPresenter, -itemToPanel.X );
      Canvas.SetTop( contentPresenter, -itemToPanel.Y );

      return contentPresenter;
    }

    private void CancelTranslateComponentsOfTransform( Transform transform )
    {
      if( transform is TransformGroup ) 
      { 
        TransformGroup group = transform as TransformGroup;

        foreach( Transform child in group.Children )
        {
          CancelTranslateComponentsOfTransform( child );
        }
      }
      else if( transform is TranslateTransform )
      {
        TranslateTransform translate = transform as TranslateTransform;

        translate.X = 0;
        translate.Y = 0;
      }
    }

    void IProvideDraggedContentPresenter.PrepareDraggedContentPresenter(ContentPresenter contentPresenter)
    {
      this.PrepareDraggedContentPresenter( contentPresenter );
    }

    void IProvideDraggedContentPresenter.ClearDraggedContentPresenter( ContentPresenter contentPresenter )
    {
      this.ClearDraggedContentPresenter( contentPresenter );
    }
    #endregion
  }
}
